{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第11章 Python的高级语法和用法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-1 枚举其实是一个类\n",
    "\n",
    "python中所有枚举类型都是enum模块下Enum类的子类。如：\n",
    "```python\n",
    "from enum import Enum\n",
    "class VIP(Enum):\n",
    "    YELLOW = 1\n",
    "    GREEN = 2\n",
    "    BLACK = 3\n",
    "    RED = 4\n",
    "```\n",
    "枚举中的标识最好全部使用大写  \n",
    "注：枚举的意义重在标签而不在于数值，使用print(VIP.YELLOW)打印结果不是1而是VIP.YELLOW，这也符合枚举的意义。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from enum import Enum\n",
    "class VIP(Enum):\n",
    "    YELLOW = 1\n",
    "    GREEN = 2\n",
    "    BLACK = 3\n",
    "    RED = 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VIP.BLACK\n"
     ]
    }
   ],
   "source": [
    "print(VIP.BLACK)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<enum 'VIP'>\n"
     ]
    }
   ],
   "source": [
    "print(VIP)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VIP.YELLOW | VIP.GREEN | VIP.BLACK | VIP.RED | "
     ]
    }
   ],
   "source": [
    "for x in VIP:\n",
    "    print(x, end=' | ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-2 枚举的优势\n",
    "\n",
    "不用枚举的话字典是一个表示种类的方式\n",
    "```python\n",
    "{'yellow':1,'green':2}\n",
    "```\n",
    "或者用类表示\n",
    "```python\n",
    "class Type():\n",
    "    yello = 0\n",
    "    ....\n",
    "```\n",
    "以上两种方法的缺点是\n",
    "+ 1、可以轻易的在代码里更改\n",
    "+ 2、没有防止标签值重复的功能  \n",
    "\n",
    "而这些枚举类型都解决了\n",
    "\n",
    "+ 枚举类型是不能通过代码改变\n",
    "+ 枚举类型不允许有两个值相同的类型出现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-3 枚举类型、枚举名称和枚举值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "from enum import Enum\n",
    "class VIP(Enum):\n",
    "    YELLOW = 1\n",
    "    GREEN = 2\n",
    "    BLACK = 3\n",
    "    RED = 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VIP.GREEN\n",
      "GREEN\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "print(VIP.GREEN) # 返回VIP.GREEN,是枚举值\n",
    "print(VIP.GREEN.name) # 返回GREEN，字符串\n",
    "print(VIP.GREEN.value) # 返回2，对对应的值类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-4 枚举的比较运算\n",
    "\n",
    "+ 1.枚举类型之间可以进行等值比较(==)，但直接和数值比较会返回False，如：VIP.GREEN == 2 返回False\n",
    "+ 2.枚举类型之间不支持大小比较操作符(>、<)的.\n",
    "+ 3.枚举类型可以进行身份比较(is)，如：VIP.GREEN is VIP.GREEN 返回 True\n",
    "+ 4.不同枚举类中的枚举类型进行比较都会返回False。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "VIP.GREEN is VIP.YELLOW"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "VIP.GREEN == VIP.YELLOW"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-5 枚举注意事项\n",
    "\n",
    "+ 1.两个枚举类型赋予的值可以相同，但此时第二个枚举类型视为首个被赋值的枚举类型的别名，建议用起名为首个的名称_ALIAS\n",
    "+ 2.若存在别名，则在遍历时别名不会被打印出来\n",
    "+ 3.打印出别名的方法可以使用内置变量`__members__`:\n",
    "   ```python\n",
    "   for v in VIP.__members__:\n",
    "         print(v)\n",
    "   ```\n",
    "   或者\n",
    "   ```python\n",
    "   for v in VIP.__members__.item():\n",
    "         print(v)\n",
    "   ```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "YELLOW\n",
      "GREEN\n",
      "BLACK\n",
      "RED\n"
     ]
    }
   ],
   "source": [
    "for v in VIP.__members__:\n",
    "    print(v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('YELLOW', <VIP.YELLOW: 1>)\n",
      "('GREEN', <VIP.GREEN: 2>)\n",
      "('BLACK', <VIP.BLACK: 3>)\n",
      "('RED', <VIP.RED: 4>)\n"
     ]
    }
   ],
   "source": [
    "for v in VIP.__members__.items():\n",
    "    print(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-6 枚举转换\n",
    "\n",
    "枚举类型建议用数字来存储在数据库中，占据更少的存储空间。但是不建议使用数字来代表枚举的类型，影响代码的可读性。\n",
    "\n",
    "如何把数据库中存储的数字转换成枚举类型？使用 枚举类名(数值) 进行“类型”转换\n",
    "```python\n",
    "from enum import Enum\n",
    "class VIP(Enum):\n",
    "    YELLOW = 1\n",
    "    GREEN = 2\n",
    "    BLACK = 2\n",
    "    RED = 4\n",
    "a = 1\n",
    "print(VIP(a))\n",
    "print(VIP(4))  # （）内的数值必须是出现过的，否则报错\n",
    "```\n",
    "输出结果：\n",
    "```shell\n",
    "VIP.YELLOW\n",
    "VIP.RED\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VIP.RED\n"
     ]
    }
   ],
   "source": [
    "print(VIP(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-7 枚举小结\n",
    "\n",
    "+ Enum： 不限制枚举类型数值的数据类型\n",
    "+ IntEnum: 限制枚举类型数值必须为整形  \n",
    "枚举类型是单例模式（23种设计模式中的一种），不能实例化  \n",
    "加上装饰器 unique ：规定两个不同的枚举类型不能取相同数值  \n",
    "```python\n",
    "from enum import IntEnum, unique\n",
    "@unique  # unique以装饰器形式使用\n",
    "class VIP(IntEnum):\n",
    "    YELL = 1\n",
    "    GREE = 1\n",
    "    BLCK = 2\n",
    "```\n",
    "Python 会报错ValueError: duplicate values found in <enum 'VIP'>: GREE -> YELL"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "duplicate values found in <enum 'VIP'>: GREE -> YELL",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-21-11506ad7af9e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0menum\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mIntEnum\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munique\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0munique\u001b[0m  \u001b[0;31m# unique以装饰器形式使用\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mclass\u001b[0m \u001b[0mVIP\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mIntEnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      4\u001b[0m   \u001b[0mYELL\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m   \u001b[0mGREE\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/lib/python3.6/enum.py\u001b[0m in \u001b[0;36munique\u001b[0;34m(enumeration)\u001b[0m\n\u001b[1;32m    832\u001b[0m                 [\"%s -> %s\" % (alias, name) for (alias, name) in duplicates])\n\u001b[1;32m    833\u001b[0m         raise ValueError('duplicate values found in %r: %s' %\n\u001b[0;32m--> 834\u001b[0;31m                 (enumeration, alias_details))\n\u001b[0m\u001b[1;32m    835\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0menumeration\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    836\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mValueError\u001b[0m: duplicate values found in <enum 'VIP'>: GREE -> YELL"
     ]
    }
   ],
   "source": [
    "from enum import IntEnum, unique\n",
    "@unique  # unique以装饰器形式使用\n",
    "class VIP(IntEnum):\n",
    "  YELL = 1\n",
    "  GREE = 1\n",
    "  BLCK = 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11-10 什么是闭包\n",
    "+ 1）函数中定义的变量作用域仅在函数中，如果找不到变量的定义，再上一级的链式作用域中去找，直到最上级\n",
    "+ 2）闭包 是 由 函数 和 函数定义时的外部环境变量构成的整体。一旦形成闭包，将不 受外部的变量的影响。闭包 = 函数 + 环境变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<cell at 0x7f0f6274c288: int object at 0x55731c4cffe0>,)\n",
      "25\n"
     ]
    }
   ],
   "source": [
    "def curve_pre():\n",
    "    a = 25  # 环境变量\n",
    "    def curve(x):\n",
    "        return a*x*x\n",
    "    return curve\n",
    "\n",
    "a = 10 # 外部变量 不会 影响到闭包内的 a 的赋值\n",
    "print(curve_pre().__closure__)\n",
    "print(curve_pre().__closure__[0].cell_contents)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
